package db

import (
	"database/sql"
	"pmservice/graph/model"

	gonanoid "github.com/matoous/go-nanoid/v2"
	"gorm.io/gorm"
)

type Patient struct {
	PID       sql.NullString
	FName     sql.NullString
	GName     sql.NullString
	Birth     sql.NullString
	Sex       sql.NullInt32
	Admit     sql.NullTime
	Discharge sql.NullTime
}

func (d *Patient) FromGQLEdit(in *model.PatientEdit) []string {
	fields := []string{}
	if in.Pid != nil {
		d.PID = sql.NullString{Valid: true, String: *in.Pid}
		fields = append(fields, "pat_p_id")
	}
	if in.Fname != nil {
		d.FName = sql.NullString{Valid: true, String: *in.Fname}
		fields = append(fields, "pat_f_name")
	}
	if in.Gname != nil {
		d.GName = sql.NullString{Valid: true, String: *in.Gname}
		fields = append(fields, "pat_g_name")
	}
	if in.Birth != nil {
		d.Birth = sql.NullString{Valid: true, String: *in.Birth}
		fields = append(fields, "pat_birth")
	}
	if in.Sex != nil {
		d.Sex = sql.NullInt32{Valid: true, Int32: int32(*in.Sex)}
		fields = append(fields, "pat_sex")
	}
	if in.Admit != nil {
		d.Admit = sql.NullTime{Valid: true, Time: *in.Admit}
		fields = append(fields, "pat_admit")
	}
	if in.Discharge != nil {
		d.Discharge = sql.NullTime{Valid: true, Time: *in.Discharge}
		fields = append(fields, "pat_discharge")
	}
	return fields
}

func (d *Patient) ToGQL() *model.Patient {
	ret := &model.Patient{}
	if d.PID.Valid {
		ret.Pid = &d.PID.String
	}
	if d.FName.Valid {
		ret.Fname = &d.FName.String
	}
	if d.GName.Valid {
		ret.Gname = &d.GName.String
	}
	if d.Sex.Valid {
		s := int(d.Sex.Int32)
		ret.Sex = &s
	}
	if d.Birth.Valid {
		ret.Birth = &d.Birth.String
	}
	if d.Admit.Valid {
		ret.Admit = &d.Admit.Time
	}
	if d.Discharge.Valid {
		ret.Discharge = &d.Discharge.Time
	}
	return ret
}

type Poc struct {
	gorm.Model
	Patient  `gorm:"embedded;embeddedPrefix:pat_"`
	UID      string
	Hospital string
	Facility string
	Code     string
	Desc     string
	ECM      sql.NullString
}

func (Poc) TableName() string {
	return "poc"
}

func (d *Poc) FromGQLNew(in *model.PocNew) {
	d.Hospital = in.Hosuid
	d.Facility = in.Facuid
	d.Code = in.Code
	if in.Desc != nil {
		d.Desc = *in.Desc
	}
}

func (d *Poc) FromGQLEdit(in *model.PocEdit) []string {
	fields := []string{}
	if in.Code != nil {
		d.Code = *in.Code
		fields = append(fields, "code")
	}
	if in.Desc != nil {
		d.Desc = *in.Desc
		fields = append(fields, "desc")
	}
	return fields
}

func (d *Poc) ToGQL() *model.PointOfCare {
	return &model.PointOfCare{
		UID:     d.UID,
		Code:    d.Code,
		Desc:    &d.Desc,
		Patient: d.Patient.ToGQL(),
	}
}

func PocGet(dbs *gorm.DB, uid string) *Poc {
	d := Poc{}
	result := dbs.Where("uid=?", uid).Take(&d)
	if result.Error != nil {
		return nil
	}
	return &d
}

// 检查账号名是否已被使用
func PocExist(dbs *gorm.DB, hosuid string, except string, code string) bool {
	d := Poc{}
	result := dbs
	if except == "" {
		result = dbs.Where("code=? AND hospital=?", code, hosuid).Take(&d)
	} else {
		result = dbs.Where("code=? AND hospital=? AND uid!=?", code, hosuid, except).Take(&d)
	}
	return result.Error == nil && result.RowsAffected > 0
}

func PocNew(dbs *gorm.DB, input *model.PocNew) (*Poc, error) {
	d := Poc{
		UID: gonanoid.Must(),
	}
	d.FromGQLNew(input)
	// 创建账号
	result := dbs.Create(&d)
	if result.Error != nil {
		return nil, result.Error
	}
	return &d, nil
}

func PocSave(dbs *gorm.DB, uid string, input *model.PocEdit) bool {
	d := Poc{}
	fields := d.FromGQLEdit(input)
	result := dbs.Where("uid=?", uid).
		Select(fields).
		Updates(&d)
	if result.Error != nil || result.RowsAffected == 0 {
		return false
	}
	return true
}

func PocDel(dbs *gorm.DB, uid string) bool {
	result := dbs.Where("uid=?", uid).Delete(&Poc{})
	return result.Error == nil && result.RowsAffected > 0
}

func PocGetByInstalledEcm(dbs *gorm.DB, ecmuid string) *Poc {
	d := Poc{}
	result := dbs.Where("ecm=?", ecmuid).Take(&d)
	if result.Error != nil {
		return nil
	}
	return &d
}

func PocInstallEcm(dbs *gorm.DB, uid string, ecmuid string) bool {
	d := Poc{
		ECM: sql.NullString{Valid: true, String: ecmuid},
	}
	fields := []string{"ecm"}
	result := dbs.Where("uid=?", uid).
		Select(fields).
		Updates(&d)
	if result.Error != nil || result.RowsAffected == 0 {
		return false
	}
	return true
}

func PocUninstallEcm(dbs *gorm.DB, uid string, ecmuid string) bool {
	d := Poc{
		ECM: sql.NullString{Valid: false},
	}
	fields := []string{"ecm"}
	result := dbs.Where("uid=? AND ecm=?", uid, ecmuid).
		Select(fields).
		Updates(&d)
	if result.Error != nil || result.RowsAffected == 0 {
		return false
	}
	return true
}

func FacGetByInstalledEcm(dbs *gorm.DB, cond model.EcmSearchCond) (ecms []*Ecm, total int64) {
	result := dbs.Where("hospital=? AND ecm IS NOT NULL", cond.Hosuid)
	if cond.Facuid != nil {
		result = result.Where("facility=?", *cond.Facuid)
	}

	pocs := []*Poc{}
	result.Model(&Poc{}).Count(&total)
	if cond.Page != nil {
		result = result.Order("id DESC").Limit(cond.Page.Number).Offset(cond.Page.Offset).Find(&pocs)
	} else {
		result = result.Order("id DESC").Find(&pocs)
	}

	if result.Error != nil {
		return nil, 0
	}

	for _, d := range pocs {
		if d.ECM.Valid {
			ecm := EcmGet(dbs, d.ECM.String)
			if ecm != nil {
				ecms = append(ecms, ecm)
			}
		}
	}

	return ecms, total
}

func PocSearch(dbs *gorm.DB, cond model.PocSearchCond) (pocs []*Poc, total int64) {
	result := dbs.Where("hospital=?", cond.Hosuid)
	if cond.Facuid != nil {
		result = result.Where("facility=?", *cond.Facuid)
	}

	result.Model(&Poc{}).Count(&total)
	if cond.Page != nil {
		result = result.Order("id DESC").Limit(cond.Page.Number).Offset(cond.Page.Offset).Find(&pocs)
	} else {
		result = result.Order("id DESC").Find(&pocs)
	}

	if result.Error != nil {
		return nil, 0
	}

	return pocs, total
}

func PocAdmitPatient(dbs *gorm.DB, uid string, input *model.PatientEdit) bool {
	poc := PocGet(dbs, uid)
	if poc == nil {
		return false
	}
	fields := poc.Patient.FromGQLEdit(input)
	result := dbs.Where("uid=?", uid).
		Select(fields).
		Updates(poc)
	if result.Error != nil || result.RowsAffected == 0 {
		return false
	}
	return true
}

func PocDischargePatient(dbs *gorm.DB, uid string) bool {
	poc := PocGet(dbs, uid)
	if poc == nil {
		return false
	}
	poc.Patient = Patient{}
	fields := []string{"pat_p_id", "pat_f_name", "pat_g_name", "pat_birth", "pat_sex", "pat_admit", "pat_discharge"}
	result := dbs.Where("uid=?", uid).
		Select(fields).
		Updates(poc)
	if result.Error != nil || result.RowsAffected == 0 {
		return false
	}
	return true
}
